package cn.tedu.jsd2207csmall.passport.service.impl;

import cn.tedu.jsd2207csmall.passport.ex.ServiceException;
import cn.tedu.jsd2207csmall.passport.mapper.AdminMapper;
import cn.tedu.jsd2207csmall.passport.mapper.AdminRoleMapper;
import cn.tedu.jsd2207csmall.passport.pojo.dto.AdminAddNewDTO;
import cn.tedu.jsd2207csmall.passport.pojo.dto.AdminLoginDTO;
import cn.tedu.jsd2207csmall.passport.pojo.entity.Admin;
import cn.tedu.jsd2207csmall.passport.pojo.entity.AdminRole;
import cn.tedu.jsd2207csmall.passport.pojo.vo.AdminListItemVO;
import cn.tedu.jsd2207csmall.passport.pojo.vo.AdminStandardVO;
import cn.tedu.jsd2207csmall.passport.repo.IAdminRedisRepository;
import cn.tedu.jsd2207csmall.passport.security.AdminDetails;
import cn.tedu.jsd2207csmall.passport.service.IAdminService;
import cn.tedu.jsd2207csmall.passport.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
public class AdminServiceImpl implements IAdminService {

    @Autowired
    AdminMapper adminMapper;
    @Autowired
    AdminRoleMapper adminRoleMapper;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    IAdminRedisRepository redisRepository;
    @Value("${csmall.jwt.secret-key}")
   private String secretKey;
    @Value("${csmall.jwt.duration-in-minute}")
    private long durationInMinute;

    @Override
    public String login(AdminLoginDTO adminLoginDTO) {
        log.info("开始处理[管理员登录]的业务,参数:{}",adminLoginDTO);
        //执行认证
        Authentication authentication
                = new UsernamePasswordAuthenticationToken(
                adminLoginDTO.getUsername(),adminLoginDTO.getPassword()
        );
        Authentication authenticateResult
                = authenticationManager.authenticate(authentication);
        log.info("认证通过,从认证器返回:{}",authenticateResult);
       //SecurityContextHolder.getContext().setAuthentication(authenticate);
        //从返回结果中获取数据
        Object principal = authenticateResult.getPrincipal();
        log.info("从返回结果的当事人类型:{}",principal.getClass().getName());
        AdminDetails adminDetails =(AdminDetails) principal;
        String username= adminDetails.getUsername();
        Long id=adminDetails.getId();
        Collection<GrantedAuthority> authorities = adminDetails.getAuthorities();
        String authoritiesJsonString = JSON.toJSONString(authorities);


        //生成JWT数据时,需要填充呆JWT中的数据
        Map<String,Object> claims=new HashMap<>();
        //claims.put("id",12138);
        claims.put("id",id);
        claims.put("username",username);
        claims.put("authoritiesJsonString",authoritiesJsonString);
        log.debug("向JWT中存入id：{}", id);
        log.debug("向JWT中存入username：{}", username);
        log.debug("向JWT中存入authoritiesJsonString：{}", authoritiesJsonString);
        //以下是生成JWT的固定代码
        Date date=new Date(System.currentTimeMillis()+durationInMinute*60*1000);
        String jwt= Jwts.builder()
                //Header
                .setHeaderParam("alg","HS256")
                .setHeaderParam("typ","JWT")
                //Payload
                .setClaims(claims)
                //Signature
                .setExpiration(date)//过期时间
                .signWith(SignatureAlgorithm.HS256,secretKey)
                .compact();
                return jwt;
    }

    @Override
    public void addNew(AdminAddNewDTO adminAddNewDTO) {

        //查询是否有已存在
        int count=adminMapper.countByUsername(adminAddNewDTO.getUsername());
        if (count>0){
            String message="用户名被占用";
            System.out.println(message);
            log.debug(message);
            throw new ServiceException(ServiceCode.ERR_SELECT,message);
        }
       count=adminMapper.countByPhone(adminAddNewDTO.getPhone());
        if (count>0){
            String message="电话被占用";
            System.out.println(message);
            log.debug(message);
            throw new ServiceException(ServiceCode.ERR_SELECT,message);
        }
        count=adminMapper.countByEmail(adminAddNewDTO.getEmail());
        if (count>0){
            String message="邮箱被占用";
            System.out.println(message);
            log.debug(message);
            throw new ServiceException(ServiceCode.ERR_SELECT,message);
        }

        Admin admin=new Admin();
        BeanUtils.copyProperties(adminAddNewDTO,admin);
        admin.setLoginCount(0);
        // TODO 从参数对象中取出密码，进行加密，再存入到Admin对象中

        int rows= adminMapper.insert(admin);
        if (rows>1){
            String message="服务器繁忙";
            System.out.println(message);
            log.debug(message);
            throw new ServiceException(ServiceCode.ERR_INSERT,message);
        }

        //同时在管理员与角色关联表中插入数据
        Long[] roleIds = adminAddNewDTO.getRoleIds();
        LocalDateTime now=LocalDateTime.now();
        AdminRole[] adminRoles=new AdminRole[roleIds.length];
        for (int i=0;i<adminRoles.length;i++){
            AdminRole adminRole=new AdminRole();
            adminRole.setAdminId(admin.getId());
            adminRole.setRoleId(roleIds[i]);
            adminRole.setGmtCreate(now);
            adminRole.setGmtModified(now);
            adminRoles[i]=adminRole;
        }
        int row = adminRoleMapper.insertBatch(adminRoles);
        if (row!=roleIds.length){
            String message="服务器繁忙请稍后再试";
            log.info(message);
            throw new ServiceException(ServiceCode.ERR_INSERT,message);
        }
    }

    @Override
    public List<AdminListItemVO> list() {
        log.info("开始处理查询列表业务");
        List<AdminListItemVO> admins = redisRepository.getList();
        if (admins.size()!=0){
            log.info("命中缓存,参数:{}",admins);
            return admins;
        }
        log.info("未命中缓存，即将向数据库查询");
        List<AdminListItemVO> list = adminMapper.list();
        Iterator<AdminListItemVO> iterator=list.iterator();
        while (iterator.hasNext()){
            AdminListItemVO adminListItemVO= iterator.next();
            if(adminListItemVO.getId()==1){
                iterator.remove();
                break;
            }
        }
        redisRepository.save(list);
        log.info("从数据库查询后存到Redis完成,参数:{}",list);
        return list;
    }

    @Override
    public void rebuildCache() {
        log.debug("开始处理【重建品牌缓存】的业务，无参数");
        log.debug("准备删除Redis缓存中的品牌数据……");
        redisRepository.deleteAll();
        log.debug("删除Redis缓存中的品牌数据，完成！");

        log.debug("准备从数据库中读取品牌列表……");
        List<AdminListItemVO> list = adminMapper.list();
        log.debug("从数据库中读取品牌列表，完成！");

        log.debug("准备将品牌列表写入到Redis缓存……");
        redisRepository.save(list);
        log.debug("将品牌列表写入到Redis缓存，完成！");

        log.debug("准备将各品牌详情写入到Redis缓存……");
        for (AdminListItemVO brandListItemVO : list) {
            Long id = brandListItemVO.getId();
            AdminStandardVO brandStandardVO = adminMapper.getStandardById(id);
            redisRepository.save(brandStandardVO);
        }
        log.debug("将各品牌详情写入到Redis缓存，完成！");
    }

    @Override
    public void delete(Long id) {
        log.info("开始处理删除管理员的业务,参数:{}",id);
        //id为1的不能删除
        if (id==1){
            String message="您无法删除超级无敌特级权限管理员";
            log.info(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,message);
        }
        AdminStandardVO adminStandardVO =adminMapper.getStandardById(id);
        if (adminStandardVO==null){
            String message="删除管理员失败,尝试删除的数据不存在";
            log.info(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,message);
        }

        int rows=adminMapper.deleteById(id);
        if (rows!=1){
            String message="服务器繁忙请稍后再试";
            log.info(message);
            throw new ServiceException(ServiceCode.ERR_DELETE,message);
        }

        // 删除管理员与角色的关联数据
        rows=adminRoleMapper.deleteAdminId(id);

        if (rows<1){
            // 是：抛出异常（ERR_DELETE：服务器忙）
            String message = "删除管理员失败，服务器忙，请稍后再尝试！";
            log.debug(message);
            throw new ServiceException(ServiceCode.ERR_DELETE, message);
        }

    }

    @Override
    public void serEnable(Long id) {
        updateEnableById(id,1);
    }

    @Override
    public void serDisEnable(Long id) {
        updateEnableById(id,0);
    }

    private void updateEnableById(Long id,Integer enable){
        String[] e={"禁用","启用"};
        log.info("开始处理{}管理员的业务",e[enable]);
        if (id==1){
            String message=e[enable]+"管理员失败,尝试访问的数据不存在";
            log.info(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,message);
        }

        AdminStandardVO adminStandardVO=adminMapper.getStandardById(id);
        if (adminStandardVO==null){
            String message="尝试访问的数据不存在";
            log.info(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND,e[enable]+"管理员失败"+message);
        }

        if (adminStandardVO.getEnable().equals(enable)){
            String message="修改失败";
            log.info(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT,e[enable]+"管理员失败,已经是"+e[enable]+"状态了");
        }

        Admin admin=new Admin();
        admin.setId(id);
        admin.setEnable(enable);
        int rows=adminMapper.update(admin);
        log.info("参数:{}",admin);
        log.info(e[enable]+"成功");
        if(rows!=1){
            String message="请稍后再试";
            log.info(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE,e[enable]+"管理员失败,请稍后再试");

        }
    }
}
